package ch.fhnw.apsi.beans;

import java.sql.SQLException;
import java.util.regex.Pattern;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.Untainted;

import ch.fhnw.apsi.db.DB;
import ch.fhnw.apsi.db.DBManager;
import ch.fhnw.apsi.db.Id;
import ch.fhnw.apsi.db.InvalidInputException;
import ch.fhnw.apsi.mail.Mailer;

public final class Firma {
  // Wir erlauben gemäss Beispiel auch mehr Zeichen.
  public static final String BUCHSTABEN = "a-zäöüëèéêïÿâàáA-ZÄÖÜËÈÉÊÏÂÀÁ";
  /**
   * Validate Email as defined by W3C for HTML5 forms.
   * <p>
   * Note: The syntax of Java Patterns is special. It needs all litaral hyphens to be escaped, so the expression differs
   * from the one given by w3c.
   * 
   * @see {@link http://www.w3.org/TR/html-markup/input.email.html}
   * 
   */
  public static final Pattern EMAIL = Pattern
      .compile("^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~\\-]+@[a-zA-Z0-9\\-]+(?:\\.[a-zA-Z0-9\\-]+)*$");
  // Vom User bei Registration definiert:
  private String firmenname;
  private String strasse;
  private String plz;
  private String stadt;
  private String email;
  // Vom System vergeben, bis dahin ist es -1:
  private long id = -1;

  /** a) Firmenname: Nur Gross- und Klein-Buchstaben und Leerzeichen (max. 20). Beispiel: Müller Martini AG; */
  @Nullable
  @Untainted
  public String getFirmenname() {
    return this.firmenname;
  }

  /** a) Firmenname: Nur Gross- und Klein-Buchstaben und Leerzeichen (max. 20). Beispiel: Müller Martini AG; */
  public void setFirmenname(@Nonnull final String firmenname) throws InvalidInputException {
    if (!firmenname.matches("[" + BUCHSTABEN + " ]{1,20}")) // unklar ob Umlaute nun erlaubt oder nicht.
      throw new InvalidInputException("Firmenname ist nicht gültig.");
    this.firmenname = firmenname;
  }

  /**
   * b) Strasse und Strassennummer : Erlaubte Zeichen: Gross- und Klein-Buchstaben, Zahlen, Punkt, Bindestrich,
   * Leerzeichen;
   */
  @Nullable
  @Untainted
  public String getStrasse() {
    return this.strasse;
  }

  /**
   * b) Strasse und Strassennummer : Erlaubte Zeichen: Gross- und Klein-Buchstaben, Zahlen, Punkt, Bindestrich,
   * Leerzeichen;
   * 
   */
  public void setStrasse(@Nonnull final String strasse) throws InvalidInputException {
    if (!strasse.matches("[" + BUCHSTABEN + "0-9.\\- ]{1,45}"))
      throw new InvalidInputException("Strasse ist nicht gültig.");
    this.strasse = strasse;
  }

  /**
   * c) PLZ : Nur Zahlen, richtige PLZ für die Schweiz, nachkontrolliert mit einem WebDienst wie z. B.:
   * http://www.postleitzahlen.ch;
   * 
   * <p>
   * Wird mit DB-Tabelle abgeglichen!
   */
  @Nullable
  @Untainted
  public String getPlz() {
    return this.plz;
  }

  /**
   * c) PLZ : Nur Zahlen, richtige PLZ für die Schweiz, nachkontrolliert mit einem WebDienst wie z. B.:
   * http://www.postleitzahlen.ch;
   * 
   * <p>
   * Wird mit DB-Tabelle abgeglichen!
   * 
   * @throws InvalidInputException
   */
  public void setPlz(@Nonnull final String plz) throws InvalidInputException {
    if (!plz.matches("[0-9]{4}"))
      throw new InvalidInputException("PLZ muss aus genau 4 Ziffern bestehen.");
    this.plz = plz;
  }

  /**
   * d) Stadt: Erlaubte Zeichen: Gross- und Klein-Buchstaben, Punkt, Bindestrich, Leerzeichen;
   */
  @Nullable
  @Untainted
  public String getStadt() {
    return this.stadt;
  }

  /**
   * d) Stadt: Erlaubte Zeichen: Gross- und Klein-Buchstaben, Punkt, Bindestrich, Leerzeichen;
   * 
   * @throws InvalidInputException
   */
  @Nonnull
  public void setStadt(@Nonnull final String stadt) throws InvalidInputException {
    // Wieso sollte Apostroph nicht erlaubt sein?! So gehen viele Schweizer Städte nicht!
    // Dies wird hier also erlaubt, um "Château-d'Oex" auch zu erlauben.
    // Ohne würde das System nämlich motzen weil die PLZ nicht stimmt.
    if (!stadt.matches("[" + BUCHSTABEN + ".\\- ']{1,45}"))
      throw new InvalidInputException("Stadt enthält ungültige Zeichen.");
    this.stadt = stadt;
  }

  /**
   * e) Email Adresse
   */
  @Nullable
  @Untainted
  public String getEmail() {
    return this.email;
  }

  /**
   * e) Email Adresse
   * 
   * @throws InvalidInputException
   */
  public void setEmail(@Nonnull final String email) throws InvalidInputException {
    if (!EMAIL.matcher(email).matches())
      throw new InvalidInputException("Ungültige E-Mail-Adresse.");
    if (!Mailer.emailMightExist(email))
      throw new InvalidInputException("Für die angegebene E-Mail-Adresse konnte kein Mail-Server gefunden werden.");
    this.email = email;
  }

  @Id
  public long getId() {
    return this.id;
  }

  public void setId(final long id) {
    this.id = id;
  }

  /**
   * Überprüft ob dies so in die DB geschrieben werden darf.
   * 
   * @throws InvalidInputException
   */
  public void validate() throws InvalidInputException {
    if (this.firmenname == null || this.strasse == null || this.plz == null || this.stadt == null || this.email == null)
      throw new InvalidInputException("Angaben sind unvollständig.");

    DB db;
    try {
      db = DBManager.getDB();
    } catch (final SQLException e1) {
      throw new InvalidInputException("Leider kann das System Ihre Anfrage momentan nicht verarbeiten.");
    }
    try {
      if (!db.checkPLZ(Integer.valueOf(this.plz), this.stadt))
        throw new InvalidInputException("PLZ und Stadt passen nicht zusammen.");
    } catch (final NumberFormatException | SQLException e) {
      throw new InvalidInputException("PLZ und Stadt können nicht validiert werden.");
    }
  }
}
